<?php

namespace PSC\Controller;
use \PSC\Controller\Request  as Request;
use \PSC\Controller\Response as Response;
use \PSC\View\View           as View;

/**
 * Controladora Frontal
 *
 * Classe utilizada para construir a requisição do sistema utilizando os
 * parâmetros informados pelo cliente. Centraliza o sistema de forma completa,
 * inicializando os objetos de requisição e resposta. Além disso, trabalha com o
 * padrão de projeto Singleton para inibir a construção de múltiplos objetos.
 *
 * @category PSC
 * @package  PSC\Controller
 */
class Front
{
    /**
     * Instância Singleton
     * @var Front
     */
    private static $_instance = null;

    /**
     * Camada de Visualização
     * @var View
     */
    protected $_view = null;

    /**
     * Responsável pela Requisição
     * @var Request
     */
    protected $_request = null;

    /**
     * Responsável pela Resposta
     * @var Response
     */
    protected $_response = null;

    /**
     * Acesso Singleton
     *
     * Método específico para acesso à instância Singleton que permanece única
     * na memória, centralizando as informações necessárias para funcionamento
     * do sistema.
     *
     * @return Front Instância Singleton Solicitada
     */
    public static function getInstance()
    {
        // Consulta de Inicialização
        if (self::$_instance === null) {
            // Construção do Elemento
            self::$_instance = new self();
        }
        // Apresentação
        return self::$_instance;
    }

    /**
     * Construtor Singleton
     *
     * Método para inicialização de instância única da camada frontal, inibindo
     * a criação de múltiplas instâncias do objeto em memória.
     */
    private function __construct()
    {
        // Configurações
        $this->setView(new View())
             ->setRequest(new Request())
             ->setResponse(new Response());
    }

    /**
     * Configuração de Visualização
     *
     * A camada de visualização é configurada utilizando métodos especializados
     * para melhorar o estudo da ferramenta. Esta visualização é aplicada
     * diretamente nas controladoras criadas durante o despacho.
     *
     * @param  View  Camada de Visualização
     * @return Front Próprio Objeto para Encadeamento
     */
    public function setView(View $view)
    {
        // Configuração
        $this->_view = $view;
        // Encadeamento
        return $this;
    }

    /**
     * Camada de Visualização
     *
     * Apresentação da camada de visualização para que seja configurada conforme
     * a necessidade do sistema. Esta sempre é passada durante a etapa de
     * despacho para a controladora atual.
     *
     * @return View Elemento Solicitado
     */
    public function getView()
    {
        // Apresentação
        return $this->_view;
    }


    /**
     * Configuração de Requisição
     *
     * Objeto que centraliza os parâmetros da requisição, tornando mais
     * acessíveis as variáveis que podem ser enviadas ao sistema. Este mesmo
     * elemento é apresentado à controladora de ações para consulta de
     * parâmetros.
     *
     * @param  Request Elemento para Configuração
     * @return Front   Próprio Objeto para Encadeamento
     */
    public function setRequest(Request $request)
    {
        // Configuração
        $this->_request = $request;
        // Encadeamento
        return $this;
    }

    /**
     * Captura de Requisição
     *
     * Apresentação do objeto que controla os parâmetros apresentados durante a
     * requisição no sistema, possuindo métodos especializados para manipular as
     * informações com maior segurança.
     *
     * @return Request Elemento Solicitado
     */
    public function getRequest()
    {
        // Apresentação
        return $this->_request;
    }

    /**
     * Configuração de Reposta
     *
     * Elemento utilizado para centralizar as informações e conteúdos de
     * resposta que serão enviados para o cliente quando a camada de
     * visualização finaliza a renderização do conteúdo. Também é responsável
     * pelo gerenciamento dos cabeçalhos do sistema.
     *
     * @param  Response Elemento para Configuração
     * @return Front    Próprio Objeto para Encadeamento
     */
    public function setResponse(Response $response)
    {
        // Configuração
        $this->_response = $response;
        // Encadeamento
        return $this;
    }

    /**
     * Captura de Resposta
     *
     * Apresenta o elemento utilizado para processar o conteúdo da resposta que
     * será enviado para o cliente ao final da renderização da visualização.
     * Também é responsável pelo controle de cabeçalhos que são enviados para o
     * cliente.
     *
     * @return Response Elemento Solicitado
     */
    public function getResponse()
    {
        // Apresentação
        return $this->_response;
    }

    /**
     * Despacho de Execução
     *
     * Execução e direcionamento de requisição para as controladoras
     * responsáveis, conforme dados apresentados pelo cliente ao sistema. Caso
     * o sistema não possua as controladoras ou ações solicitadas, a
     * controladora de erros é solicitada.
     *
     * @return Front Próprio Objeto para Encadeamento
     */
    public function dispatch()
    {
        // Consulta de Parâmetros
        $controller = $this->getRequest()->getParam('controller', 'index');
        $action     = $this->getRequest()->getParam('action', 'index');
        // Execução
        $this->_execute($controller, $action);
        // Captura de Renderização
        $content = $this->getView()->render("$controller/$action.phtml");
        // Renderização em Dois Passos
        $this->getView()->setParam('content', $content);
        // Construção do Layout
        $content = $this->getView()->render('layout.phtml');
        // Configuração
        $this->getResponse()->setBody($content);
        // Enviar Conteúdo
        $this->getResponse()->output();
    }

    /**
     * Execução Interna
     *
     * Recebendo os parâmetros de requisição, inicializa as controladoras de
     * ação responsáveis. Caso as controladoras não existam, sempre será
     * solicitada a controladora de erros utilizando o próprio método de forma
     * recursiva.
     *
     * @param  string $controller Nome da Controladora
     * @param  string $action     Nome da Ação
     * @param  bool   $handle     Manipular Requisição caso Elementos Inválidos
     * @return Front  Próprio Objeto para Encadeamento
     */
    protected function _execute($controller, $action, $handle = true)
    {
        // Conversão
        $handle     = (bool) $handle;
        // Construção de Elementos
        $controller = 'Controller\\' . ucfirst($controller);
        $action     = $action . 'Action';
        // Verificação
        if (class_exists($controller)
            && in_array($action, get_class_methods($controller))) {
            // Construção
            $container = new $controller($this);
            // Passagem de Visualização
            $container->view = $this->getView();
            // Chamada de Ação
            $container->$action();
        } elseif ($handle) {
            $this->_execute('error', 'error', false);
        }
        // Encadeamento
        return $this;
    }
}

